﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Infinity.Common
{
	/// <summary>
	/// Ensure class for checking safely null references
	/// </summary>
	public static class Ensure
	{
		private const string VARIABLE_OF_IS_NULL_MESSAGE = "Variable of {0} is null!";
		private const string ARGUMENT_IS_NULL_MESSAGE = "{0} argument is null!";
		private const string STRING_CANNOT_BE_NULL_OR_BLANK_MESSAGE = "String cannot be null or blank!";
		private const string GIVEN_OBJECT_IS_NOT_TYPE_OF_MESSAGE = "Given object is not type of {0}!";
		private const string GIVEN_OBJECT_IS_NOT_DESCENDANT_OF_MESSAGE = "Given object is not descendant of {0}!";

		/// <summary>
		/// It checks the given obj is not null.
		/// </summary>
		public static T NotNull<T>(string name, T obj)
			where T : class
		{
			if (obj == null)
			{
				if (string.IsNullOrWhiteSpace(name))
					throw new NullReferenceException(string.Format(VARIABLE_OF_IS_NULL_MESSAGE, typeof(T).FullName));
				throw new NullReferenceException(string.Format(ARGUMENT_IS_NULL_MESSAGE, name));
			}
			return obj;
		}

		/// <summary>
		/// It checks the given obj is not null
		/// </summary>
		public static T NotNull<T>(T obj)
			where T : class
		{
			return NotNull(string.Empty, obj);
		}

		/// <summary>
		/// It throws NullReferenceException if the 'text' is null or blank
		/// </summary>
		public static string NotBlank(string text)
		{
			if (String.IsNullOrWhiteSpace(text))
				throw new NullReferenceException(STRING_CANNOT_BE_NULL_OR_BLANK_MESSAGE);
			return text;
		}

		/// <summary>
		/// It throws exceptions if given parameter is not type of T. Otherwise it returns given parameter.
		/// </summary>
		public static T TypeOf<T>(object obj)
		{
			if (obj.GetType() != typeof(T))
				Error.ThrowException(string.Format(GIVEN_OBJECT_IS_NOT_TYPE_OF_MESSAGE, typeof(T).FullName));
			return (T)obj;
		}

		/// <summary>
		/// It throws exceptions if given parameter is not type of 'type'. Otherwise it returns given parameter.
		/// </summary>
		public static T TypeOf<T>(T obj, Type type)
		{
			if (obj.GetType() != type)
				Error.ThrowException(string.Format(GIVEN_OBJECT_IS_NOT_TYPE_OF_MESSAGE, type.FullName));
			return obj;
		}

		/// <summary>
		/// It throws exceptions if given parameter is not descendant of T. Otherwise it returns given parameter.
		/// </summary>
		public static T DescendantOf<T>(object obj)
			where T : class
		{
			if (!(obj is T))
				Error.ThrowException(string.Format(GIVEN_OBJECT_IS_NOT_DESCENDANT_OF_MESSAGE, typeof(T).FullName));
			return obj as T;
		}

		/// <summary>
		/// It throws exception if given array is empty.
		/// </summary>
		public static T NotEmpty<T>(T array)
			where T : class, IEnumerable
		{
			NotNull("array", array);

			foreach (var obj in array)
				return array;

			throw Error.Exception("The array cannot be empty!");
		}

		/// <summary>
		/// It throws exception if given array is not empty.
		/// </summary>
		public static T Empty<T>(T array)
			where T : class, IEnumerable
		{
			NotNull(array);

			foreach (var obj in array)
				Error.ThrowException("The array must be empty!");

			return array;
		}

		/// <summary>
		/// It throws exception if given condition is not true.
		/// </summary>
		public static void True(bool condition)
		{
			True(condition, "The condition is not true!");
		}

		/// <summary>
		/// It throws exception if given condition is not true.
		/// </summary>
		private static void True(bool condition, string message)
		{
			Condition(condition, true, message);
		}

		/// <summary>
		/// It throws exception if given condition is not true.
		/// </summary>
		public static void True(Func<bool> condition)
		{
			True(condition, "The condition is not true!");
		}

		/// <summary>
		/// It throws exception if given condition is not true.
		/// </summary>
		public static void True(Func<bool> condition, string message)
		{
			Condition(condition, true, message);
		}

		/// <summary>
		/// It throws exception if given condition is not false.
		/// </summary>
		public static void False(Func<bool> condition)
		{
			False(condition, "The condition is not false!");
		}

		/// <summary>
		/// It throws exception if given condition is not false.
		/// </summary>
		public static void False(Func<bool> condition, string message)
		{
			Condition(condition, false, message);
		}

		/// <summary>
		/// It throws exception if given condition is not false.
		/// </summary>
		public static void False(bool condition)
		{
			False(condition, "The condition is not false!");
		}

		/// <summary>
		/// It throws exception if given condition is not false.
		/// </summary>
		private static void False(bool condition, string message)
		{
			Condition(condition, false, message);
		}


		public static void Condition(Func<bool> condition, bool expected, string message)
		{
			NotNull(condition);
			Condition(condition(), expected, message);
		}

		public static void Condition(bool condition, bool expected, string message)
		{
			NotNull(message);

			if (condition != expected)
				Error.ThrowException(message);
		}
	}
}